# -*- coding: utf-8 -*-
import re
import json
from collections import Counter
from typing import AsyncGenerator, Set, List, Dict

from loguru import logger
from zai import ZhipuAiClient
import jieba
from langchain_core.documents import Document

import sys

sys.path.append("/home/yoka-tang")
from configs import KB_CHAT_TEMPLATE_BASE_FILENAMES, KB_CHAT_TEMPLATE
from document_splitter.chinese_text_splitter import ChineseRecursiveTextSplitter


async def invoke_glm(query: str, chunks: Dict) -> AsyncGenerator[str, None]:
    """
    异步生成器，用于流式调用GLM模型
    """
    API_KEY = "5481525309f14d0fb8f13d22c0e17ce9.MFn1IkkXP6ugQrky"
    client = ZhipuAiClient(api_key=API_KEY)

    context = "\n\n".join([f"[{i+1}] {chunk['content']}" for i, chunk in enumerate(chunks)])

    prompt = f"""
        【参考资料】
        {context}

        【用户问题】
        {query}

        【参考资料使用要求】
        1. 请优先判断参考资料是否与问题相关，若参考资料与问题相关，则优先整合参考内容作答，并在引用处标注来源，如：[1]、[2]。
        2. 在保证回答准确性的前提下，尽量引用所有的参考资料，每个引用的地方最多引用3个参考资料。
        3. 若参考资料为空，你可以基于你自身的知识，根据用户的问题给出一定的回答，但是需要在内容结尾说明："以上内容为模型自主生成，请谨慎参考。"。
        4. 判断参考资料是否与答案相关，如果相关，则添加引用标签；如果不相关，则不需要引用此条参考资料，直接忽略。

        【回答要求】
        1. 回答应简明扼要、逻辑清晰，避免冗长或重复表述。
        2. 回答需直接进入主题，不得出现问候语、自我介绍或寒暄。
        3. 回答必须使用 Markdown 格式输出，表格、代码、公式等内容请使用 Markdown 格式输出，其余内容段落式规范输出。
        4. 回答需针对用户问题进行回答，不要出现与用户问题无关的内容，保证回答的准确性，可基于你自身的知识，酌情添加一些自主生成的内容，丰富回答。
        5. 仅对行业法规名称添加《》，如《xx法》、《xx标准》；国家标准编号（如GB 16994）、文档名称、表格名称等无需添加书名号。
        6. 如果参考资料中包含图片链接（Markdown格式：![image_id](url)），在回答中需要引用图片时，请保持该Markdown格式输出，同时确保URL正确。
        7. 仅回答港口及安全生产相关问题；若问题无关，请礼貌建议用户重新提问："您的提问与港口安全生产无关，请重新提问。"。

        【重点要求】
        1. 回答必须使用 Markdown 格式输出，表格、代码、公式等内容请使用 Markdown 格式输出，其余内容段落式规范输出。
        2. 如果参考资料中包含图片链接（Markdown格式：![image_id](url)），在回答中需要引用图片时，请保持该Markdown格式输出，同时确保URL正确。
        3. 严禁自己编造参考资料中不存在的图片链接，如果参考资料中不存在图片链接，则不要引用图片。
    """
    try:
        # 直接调用API获取流对象（创建流对象通常很快，不会阻塞）
        response = client.chat.completions.create(
            model="glm-4.6",
            messages=[
                {"role": "system", "content": "你是港口AI助手，专注于回答港口安全生产相关问题。"},
                {"role": "user", "content": prompt}
            ],
            thinking={"type": "enabled"},
            stream=True
        )
        
        reasoning_content = ''
        yield '<think>'
        in_reasoning_phase = True
        buffered_answer = ''
        # 思考阶段先流式输出reasoning；检测到答案出现后，立即闭合think并开始流式输出答案
        for chunk in response:
            if not chunk.choices:
                continue

            rc = chunk.choices[0].delta.reasoning_content
            ac = chunk.choices[0].delta.content

            if in_reasoning_phase:
                if rc:
                    reasoning_content += rc
                    yield rc
                if ac:
                    # 出现答案内容，认为推理结束，切换到答案阶段
                    in_reasoning_phase = False
                    yield '</think>'
                    if buffered_answer:
                        yield buffered_answer
                        buffered_answer = ''
                    yield ac
            else:
                if ac:
                    yield ac
                # 若后续异常出现rc，直接忽略

        if in_reasoning_phase:
            # 如果流结束仍在思考阶段（无答案），补上闭合标签
            yield '</think>'

    except Exception as e:
        logger.error(f"GLM调用失败: {e}")
        import traceback
        logger.error(traceback.format_exc())
        yield None


def prepare_prompt(query: str, references: List) -> str:
    """
    遍历references，如果存在file_id出现次数大于2的情况，则将所有file_id>2的文件文本拼成一个字符串，并且将所有文件文本拼成字符串content_str
    如果不存在file_id出现次数大于2的情况，则跳过
    """
    file_id_counts = Counter(ref.get("file_id") for ref in references)

    # 检查是否有file_id出现次数大于2
    has_frequent_files = any(count > 2 for count in file_id_counts.values())

    if has_frequent_files:
        # 找出出现次数大于2的file_id
        frequent_file_ids = {file_id for file_id, count in file_id_counts.items() if count > 2}

        content_str = ""
        important_str = ""
        for ref in references:
            content_str += f'文件名：{ref.get("file_name")}\n内容：{ref.get("content")}\n\n'
            if ref.get("file_id") in frequent_file_ids:
                important_str += f'文件名：{ref.get("file_name")}\n内容：{ref.get("content")}\n\n'

        return KB_CHAT_TEMPLATE_BASE_FILENAMES \
            .replace("{que}", query) \
            .replace("{context}", content_str) \
            .replace("{important}", important_str)
    else:
        # 没有高频文件，使用普通模板
        content_str = '\n'.join([f'文件名：{ref.get("file_name")}\n内容：{ref.get("content")}\n' for ref in references])
        return KB_CHAT_TEMPLATE.replace("{que}", query).replace("{context}", content_str)


def replace_attach_id(document: Document, token: str, nginx_prefix: str = '/api2') -> str:
    """
    将 Document.page_content 中的 "<-- image_001 -->" 与 "<-- table_001 -->" 替换为实际内容：
    - image 占位替换为 <img src="/{nginx_prefix}/image/{content}" onerror="this.onerror=null; this.src='{image_url}';" alt="文档图片" style="max-width: 100%; height: auto;">
    - table 占位替换为 metadata.tables 中对应的 HTML 内容
    """

    # image_tag_tpl = '<img src=\'{primary_url}\' onerror="this.onerror=null; this.src=\'{image_url}\';" alt="文档图片" style="max-width: 100%; height: auto;">'
    image_tag_tpl = '<img src=\'{primary_url}\' alt="文档图片" style="max-width: 100%; height: auto;">'

    content = document.page_content or ""
    metadata = getattr(document, 'metadata', {}) or {}

    # 构建映射：image_id -> image_entry, table_id -> table_html
    images_map: Dict[str, Dict] = {}
    for img in (metadata.get('images') or []):
        image_id = img.get('image_id')
        if image_id:
            images_map[image_id] = img

    tables_map: Dict[str, str] = {}
    for tb in (metadata.get('tables') or []):
        table_id = tb.get('table_id')
        table_html = tb.get('content')
        if table_id and table_html is not None:
            tables_map[table_id] = table_html

    if not any([images_map, tables_map]):
        return content

    # 替换图片占位符
    def _replace_image(m: re.Match) -> str:
        img_id = m.group(1)
        info = images_map.get(img_id)
        if not info:
            return m.group(0)
        # content 字段中存的是形如 "/image/xxx.jpg"，此处拼到 /{nginx_prefix}/image/{content}
        # 如果 content 含有前导斜杠，拼接时保持既有相对路径片段
        content_path = (info.get('id') or '').lstrip('/')
        primary_url = f"{nginx_prefix}/image/{content_path}"
        # return image_tag_tpl.format(primary_url=primary_url, image_url=image_url)
        return image_tag_tpl.format(primary_url=primary_url)

    # 替换两种格式的图片占位符
    # 格式1: <-- image_001 -->
    content = re.sub(r'<--\s*(image_\d+)\s*-->', _replace_image, content)

    # 格式2: <img src="img_001">（更健壮：大小写不敏感，允许空白、跨行、连字符/点扩展名）
    # 允许前面带一个或多个分隔符（中点·、空白或 <br/> 标签）
    # content = re.sub(r'(?:[·\s]|<br\s*/?>)*<img[^>]+src=["\']((?:image|img)_\w+)["\'][^>]*>', _replace_image, content, flags=re.IGNORECASE)
    content = re.sub(r'<img[^>]+src=["\']((?:image|img)_\w+)["\'][^>]*>', _replace_image, content, flags=re.IGNORECASE)

    # 清理紧跟在 <img ...> 之后的多余实体 &gt;（防止渲染出多余的 > 符号）
    # content = re.sub(r'(<img[^>]*>\s*)&gt;?\s*', r'\1', content, flags=re.IGNORECASE)

    content = re.sub(r"<!--\s*(table_\d+)\s*-->", "", content)

    return content


def remove_attach_id(text: str)->str:
    text = re.sub(r'<--\s*(image|table)_\d+\s*-->', '', text)
    text = re.sub(r'<img[^>]+src=["\']((?:image|img)_\w+)["\']', '', text)
    return text


def extract_markdown_tables(text: str) -> List[Dict[str, str]]:
    """
    从文本中提取Markdown表格和文本内容

    参数:
        text: 包含Markdown表格和文本的字符串

    返回:
        字典列表，每个字典包含type和content字段
        type可以是"table"或"text"
    """
    # 正则表达式匹配Markdown表格
    table_pattern = r'(\|.*\|\s*\n\|[-:|]+\s*\n(?:\|.*\|\s*\n)*)'

    # 查找所有表格的位置
    tables = []
    for match in re.finditer(table_pattern, text):
        start, end = match.span()
        tables.append((start, end, match.group()))

    # 如果没有找到表格，返回整个文本
    if not tables:
        return [{"type": "text", "content": text}]

    # 构建结果列表
    result = []
    current_pos = 0

    # 处理每个表格及其前后的文本
    for i, (start, end, table_content) in enumerate(tables):
        # 添加表格前的文本
        if start > current_pos:
            text_before = text[current_pos:start].strip()
            if text_before:
                result.append({"type": "text", "content": text_before})

        # 添加表格
        result.append({"type": "table", "content": table_content.strip()})

        # 更新当前位置
        current_pos = end

        # 如果是最后一个表格，添加表格后的文本
        if i == len(tables) - 1 and current_pos < len(text):
            text_after = text[current_pos:].strip()
            if text_after:
                result.append({"type": "text", "content": text_after})

    return result


def response_source_text(response_text: str, references: List[Dict]) -> str:
    def is_similar(sentence_chunks: Set, reference_chunks: Set, sub_threshold: float = 0.8) -> bool:
        score = len(list(sentence_chunks & reference_chunks)) / (
            len(list(sentence_chunks)) if len(list(sentence_chunks)) < len(list(reference_chunks)) else len(
                list(reference_chunks)))
        return True if score > sub_threshold else False

    if not len(references):
        return response_text

    sentences = []
    text_list = extract_markdown_tables(response_text)
    splitter = ChineseRecursiveTextSplitter(150, 0)
    for i in text_list:
        if i.get("type") != "table":
            s = splitter.split_text(i.get("content"))
            sentences.extend(s)
        else:
            sentences.append(i.get("content"))

    sentences_chunks = [set(jieba.cut(sentence, cut_all=True)) for sentence in sentences]

    results = {i: [] for i in range(len(sentences))}
    for r_index, reference in enumerate(references):
        text = reference.get("content", "")
        reference_chunks = set(jieba.cut(text, cut_all=True))
        for s_index, sentence_chunks in enumerate(sentences_chunks):
            if is_similar(sentence_chunks, reference_chunks, 0.6):
                results[s_index].append(r_index + 1)

    if not len(results):
        return response_text

    res = ""
    for index, cites in results.items():
        if len(cites):
            res += f"{sentences[index]}\n<cite>{','.join([str(i) for i in cites])}</cite>\n\n"
        else:
            res += f"{sentences[index]}\n\n"
    return res


if __name__ == '__main__':
    doc = Document(page_content="""<table border="1" ><tr>
<td rowspan="2">联合国 编号</td>
<td rowspan="2">名称和说明</td>
<td rowspan="2">英文名称和说明</td>
<td rowspan="2">类别 或项别</td>
<td rowspan="2">次要 危险性</td>
<td rowspan="2">包装 类别</td>
<td rowspan="2">特殊 规定</td>
<td rowspan="2">有限数量</td>
<td rowspan="2">例外 数量</td>
<td colspan="2">包装和中型散装容器</td>
<td colspan="2">可移动罐柜和散装容器</td>
</tr><tr>
<td>包装指南</td>
<td>特殊包 装规定</td>
<td>指南</td>
<td>特殊 规定</td>
</tr><tr>
<td>(1) </td>
<td>(2) </td>
<td>(3) </td>
<td>(4) </td>
<td>(5) </td>
<td>(6) </td>
<td>(7) </td>
<td>(8) </td>
<td>(9) </td>
<td>(10) </td>
<td>(11) </td>
<td>(12) </td>
<td>(13) </td>
</tr><tr>
<td rowspan="2">1987 </td>
<td rowspan="2">醇类，未另作规定的</td>
<td rowspan="2">ALCOHOLS,N.O.S. </td>
<td>3 </td>
<td>-</td>
<td>II </td>
<td>274 </td>
<td>1L </td>
<td>E2 </td>
<td>P001 IBC02 </td>
<td>-</td>
<td>T7 </td>
<td>TP1 TP8 TP28 </td>
</tr><tr>
<td>3 </td>
<td>-</td>
<td>III </td>
<td>223 274 </td>
<td>5L </td>
<td>E1 </td>
<td>P001 IBC03 LP01 </td>
<td>-</td>
<td>T4 </td>
<td>TP1 TP29 </td>
</tr><tr>
<td rowspan="3">1988 </td>
<td rowspan="3">醛类，易燃，毒性，未另作 规定的</td>
<td rowspan="3">ALDEHYDES, FLAMMABLE, TOXIC,N.O.S. </td>
<td>3 </td>
<td>6.1 </td>
<td>I </td>
<td>274 </td>
<td>0 </td>
<td>E0 </td>
<td>P001 </td>
<td>-</td>
<td>T14 </td>
<td>TP2 TP13 TP27 </td>
</tr><tr>
<td>3 </td>
<td>6.1 </td>
<td>II </td>
<td>274 </td>
<td>1L </td>
<td>E2 </td>
<td>P001 IBC02 </td>
<td>-</td>
<td>T11 </td>
<td>TP2 TP27 </td>
</tr><tr>
<td>3 </td>
<td>6.1 </td>
<td>III </td>
<td>223 274 </td>
<td>5L </td>
<td>E1 </td>
<td>P001 IBC03 </td>
<td>-</td>
<td>T7 </td>
<td>TP1 TP28 </td>
</tr></table>""", metadata={"images": [], "tables": [],})
    r =replace_attach_id(document=doc, token='111')
    print(r)
#     response = """#### 白色虚线图例及内涵说明
# **1. 定义与作用**
# 白色虚线为车行道边缘标线，**指示车辆可临时越线行驶**的边界线。核心作用：
# - 允许短时跨越线段（如进出路口、让车等）
# - 跨越时需主动避让其他正常行驶的车辆、非机动车和行人

# **2. 设置场景**
# | **场景类型**       | **特征描述**                         |
# |--------------------|--------------------------------------|
# | 出入口/交叉口      | 车辆需频繁变道或汇入                 |
# | 允许路边停车路段   | 提示车辆可临时靠边停车               |
# | 城市密集出入口道路 | 相邻出入口间距≤100m时连续设置        |

# **3. 技术参数**
# | **项目**         | **标准值**                          |
# |------------------|-------------------------------------|
# | 线宽             | 15cm/20cm（农村道路可采用10cm）     |
# | 虚线线段长       | 200cm                               |
# | 虚线间隔长       | 400cm                               |

# **4. 与白色虚实线的关联**
# - **虚线侧允许越线**，实线侧禁止越线（如公交车站、路边停车路段）
# - **虚实线间距**：15cm~20cm，虚线线段及间隔同上表

# **5. 港口安全提示**
# - 跨越虚线前需观察后视镜并提前打转向灯；
# - 港口作业区需注意大型机械和集装箱运输车动态；
# - 与《港口道路标线设置规范》中的**黄色实线**（禁止越线）形成互补警示体系。

# > 注：图6箭头方向表示车流行驶方向，实际应用需结合港口平面图综合判断。"""
#     ref = [
#   {
#     "file_id": "e513aa48-76a1-11f0-bcfa-00163e0c52c3",
#     "file_name": "道路交通标志和标线 第3部分：道路交通标线 GB 5768.3-2009",
#     "score": "0.9166126",
#     "content": "#### 4.5.3 车行道边缘白色虚线用以指示车辆可临时越线行驶的车行道边缘。跨越边缘虚线行驶的车辆应避让其他正常行驶的车辆、非机动车和行人。\n在出入口、交叉口及允许路边停车路段等允许机动车跨越边缘线的地方，可设置车行道边缘白色虚线。城市道路相邻出入口间距小于等于100m时，车行道边缘虚线可连续设置。\n车行道边缘白色虚线的一般线宽为15cm或20cm，交通量非常小的农村公路、专属专用道路等特殊应用情况下，车行道边缘白色虚线的线宽可采用10cm。\n车行道边缘白色虚线的虚线线段及间隔长分别为200cm和400cm，如图6所示（图中箭头仅表示车流行驶方向）。\n<-- image_006 -->\n**图6线6 车行道边缘白色虚线**\n4.5.4 车行道边缘白色虚实线的虚线侧允许车辆越线行驶，实线侧不允许车辆越线行驶，用以规范车辆行驶轨迹。在必要的地点，如公交车站临近路段、允许路边停车路段等，可设置车行道边缘白色虚实线。跨线行驶的车辆，应避让其他正常行驶的车辆、非机动车和行人。\n车行道边缘白色虚实线的一般线宽为15cm或20cm，交通量非常小的农村公路、专属专用道路等特殊应用情况下，车行道边缘白色虚实线的线宽可采用10cm。\n车行道边缘白色虚实线的虚实线间距为15cm~20cm，虚线线段及间隔长分别为200cm和400cm，如图7所示（图中箭头仅表示车流行驶方向）。\n<-- image_007 -->\n**图7线7 车行道边缘白色虚实线**\n4.5.5 机动车单向行驶且非机动车双向行驶的路段，在机动车道与对向非机动车道之间应施划黄色单实线作为车行道边缘线。单向行驶的道路左边缘应施划黄色单实线为车行道边缘线。黄色单实线一般线宽为15cm或20cm，交通量非常小的农村公路、专属专用道路等特殊应用情况下，线宽可采用10cm，如图8所示（图中箭头仅表示车流行驶方向）。\n黄色实线车行道边缘线可采用振动标线的形式。"
#   }
# ]
#     # ref = json.loads(ref)
#     r = response_source_text(response, ref)
#     print(r)

#     ref = [{"score": "0.5394956",
#             "content": "## 危险化学品企业安全生产标准化\n\n## 通用规范\n\n### 1 范围\n本文件规定了危险化学品企业开展安全生产标准化工作的基本要求及管理要求。\n本文件适用于危险化学品生产企业、使用危险化学品从事生产的化工企业，以及储存危险化学品经营的企业（以下简称企业）。",
#             "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"},
#             {"score": "0.5363121",
#              "content": "##### 5.10.2 化学品安全技术说明书和安全标签\n5.10.2.1 企业应对其生产、经营的化学品提供化学品安全技术说明书，并在其包装上粘贴或者拴挂与包装内化学品相符的化学品安全标签，安全标签应符合GB15258等要求。\n5.10.2.2 采购化学品时，企业应索取相关的化学品安全技术说明书和安全标签。\n##### 5.10.3 储存安全\n5.10.3.1 企业应按照规定的储存方式、储存数量储存危险化学品，对危险化学品出入库进行核查登记。\n5.10.3.2 企业储存剧毒化学品和第一类非药品类易制毒化学品，应实行双人收发、双人保管。\n5.10.3.3 企业应将储存剧毒化学品数量、地点以及管理人员的情况，按规定进行备案。\n##### 5.10.4 危险化学品重大危险源\n5.10.4.1 企业应按照GB 18218对生产、使用或储存的危险化学品进行辨识、分级，建立危险化学品重大危险源档案，定期检测、评估、监控，并制定应急预案。\n5.10.4.2 企业应将危险化学品重大危险源及有关安全措施、应急措施，按规定进行备案。\n5.10.4.3 涉及重点监管危险化工工艺、重点监管危险化学品和危险化学品重大危险源的生产、储存装置应开展危险与可操作性（HAZOP）分析。构成危险化学品重大危险源的生产单元、储存单元应按照GB 17681设置安全仪表系统（SIS）等安全监控系统。\n5.10.4.4 企业应定期对危险化学品重大危险源的设备设施进行检测、检验、维护、保养，保证危险化学品重大危险源的设备设施有效、可靠运行。\n5.10.4.5 企业应明确每一处危险化学品重大危险源安全包保责任人，利用双重预防数字化系统如实记录安全包保责任人履职情况。\n##### 5.10.5 分析化验室（实验室）安全\n5.10.5.1 企业分析化验室（实验室）应配备符合安全、消防相关技术标准要求的报警、防护和消防设施。\n5.10.5.2 企业分析化验室（实验室）化学试剂应按性质分类规范存放，化学品（含配制试剂）标签应完整清晰。\n5.10.5.3 企业应定期检查维护排风系统，确保通风能力与分析化验室（实验室）运行情况相适应。\n##### 5.10.6 证实方法\n通过查验的方式，确定化学品物理危险性分类报告、危险化学品管理档案、危险化学品登记证、化学品安全技术说明书和安全标签、危险化学品重大危险源资料、安全包保责任人履职等与5.10所有要求的符合性。\n#### 5.11 变更管理\n##### 5.11.1 分类和分级\n5.11.1.1 企业应建立变更分类标准，根据变更的内容、期限和影响对变更进行分类管理。\n5.11.1.2 企业应根据变更带来的安全风险大小，对变更进行分级管理，并确定变更审批流程。\n5.11.1.3 企业应对变更申请、安全风险评估、审批与实施、验收等过程进行规范管理。\n##### 5.11.2 申请和批准\n5.11.2.1 企业任何变更实施前，申请部门应按要求办理变更申请，对变更产生的安全风险及实施过程\n中的安全风险进行评估和管控。\n5.11.2.2 变更申请部门应将变更申请报主管部门审核，由相应负责人分级批准。\n5.11.2.3 审批人员应审查变更必要性、变更流程与管理制度的符合性、变更的安全风险评估准确性以及措施的有效性。\n##### 5.11.3 变更实施\n5.11.3.1 企业应按照审批确定的内容和范围实施变更，在实施过程中应落实安全风险控制措施。\n5.11.3.2 企业应将变更的实施情况及变更结果及时告知企业相关部门。\n5.11.3.3 企业应对变更可能影响到的相关人员进行培训，培训内容应包括变更内容及操作方法、变更中可能的安全风险和影响、安全风险的管控措施等。",
#              "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"},
#            {"score": "0.53395027",
#             "content": "3.5\n**危险化学品重大危险源安全监控系统 safety monitoring system of major hazard installations for** **hazardous chemicals**\n用于危险化学品重大危险源安全监控的软硬件设施。\n注：危险化学品重大危险源安全监控系统包括基本过程控制系统、安全仪表系统、气体检测报警系统、电视监视系统、雷电预警系统、接地电阻监测系统中的一个或多个。",
#             "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"},
#             {"score": "0.52729046",
#              "content": "4.1.4 重大危险源和涉及重点监管的危险化学品的储运设施紧急切断、自动联锁关闭等自动化控制系统，可燃气体和有毒气体检（探）测报警器，以及重大危险源的温度、压力、液位、流量等在线监测监控系统应保持正常运行。\n4.1.5 安全标志、警示标识以及工业管道的基本识别色、识别符号和安全标识的设置应按照GB 2893、GB 2894、GB 7231的规定执行。安全标志和警示标识等每半年应至少检查1次。\n4.1.6 启用、停用油气回收设施应进行开车、停车条件确认，按操作规程操作，并定期进行巡检。\n4.1.7 软管存放应设置专门区域，存放时应加装盲板。\n4.1.8 不应占用、堵塞、封闭疏散通道、安全出口、消防车通道。",
#              "file_id": "a1229de0-733f-11f0-9f08-00163e0c52c3"},
#            {"score": "0.5252702",
#             "content": "**4.4** 企业应对装卸设备设施、消防设备设施、防雷与静电接地装置、通信设备、防爆设施、应急救援设施及个体防护装备等进行定期检查、维护，确认其处于适用状态。\n**4.5** 企业应根据装卸固体散装危险货物的危险特性和安全要求，为从业人员配备相应的防护用品，并指导从业人员规范穿戴和使用。\n**4.6** 固体散装危险化学品港口作业应在具有从业资格的装卸管理人员的现场指挥或监控下进行。\n**4.7** 固体散装危险货物应储存在港区专用的库场，并由专人负责管理。",
#             "file_id": "03cc6cd8-7353-11f0-8103-00163e0c52c3"},
#             {"score": "0.52271825",
#              "content": "## 4 一般要求\n### 4.1 基本规定\n4.1.1 港口石油化工库区（以下简称“库区”）应根据所储存石油化工产品的危险性实行分区分类管理，并按照GB 50074、GB 50160、GB 50737等的相关要求控制安全距离。\n4.1.2 储罐不应超温、超压、超液位储存，储存货种应与作业许可一致。\n4.1.3 安全仪表、气体监测、消防控制、自动联锁保护和紧急停车等系统应定期进行测试。防爆设备、静电接地系统、防雷装置、工艺设备设施、消防设备设施、防腐设施和个体防护装备等应进行日常检查和定期检查、维护、测试或检测。",
#              "file_id": "a1229de0-733f-11f0-9f08-00163e0c52c3"},
#            {"score": "0.5220367",
#             "content": "3.4\n**危险化学品重大危险源 major hazard installations for hazardous chemicals**\n长期地或临时地生产、储存、使用和经营危险化学品，且危险化学品的数量等于或超过临界量的单元。",
#             "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"},
#             {"score": "0.5206994",
#              "content": "##### 5.7.6 现场规范化管理\n5.7.6.1 企业应对现场安全标识、工器具、消防器材及检维修作业现场等实施定置规范管理。\n5.7.6.2 企业应在生产现场设置安全标识、工艺设备标识。\n5.7.6.3 企业应保持应急通道畅通，保证人员紧急撤离和消防车辆通行。\n##### 5.7.7 证实方法\n通过查验的方式，确定操作规程和工艺卡片、操作记录、开停车管理、报警管理、异常工况处置等与5.7所有要求的符合性。\n#### 5.8 作业安全\n##### 5.8.1 作业许可管理\n5.8.1.1 企业应明确作业许可范围、作业许可类别分级与审批权限等，规范管理作业许可，确保作业安全。\n5.8.1.2 企业应对GB 30871规定的特殊作业以及企业认为需要通过许可管理的其他作业实施许可管理。\n##### 5.8.2 作业安全风险分析\n**5.8.2.1** 企业应在特殊作业实施前开展作业安全风险分析。当工艺条件、作业条件、作业方式或作业环境改变时，或发生事故后，应重新进行安全风险分析。\n9\n5.8.2.2 企业应根据安全风险分析结果，制定相应的控制措施。\n##### 5.8.3 作业过程安全管控\n5.8.3.1 企业应设置监护人对实施许可管理的作业活动进行现场监护，监护人应具备基本救护技能和作业现场的应急处理能力。\n5.8.3.2 构成危险化学品重大危险源的企业应建设应用特殊作业审批与作业管理场景功能开展特殊作业管控。\n5.8.3.3 企业应组织作业现场检查，发现隐患及时采取措施消除。\n5.8.3.4 作业完毕，企业应及时清理作业现场，并组织验收确认。\n##### 5.8.4 证实方法\n通过查验的方式，确定作业许可管理、作业现场活动和监护情况等与5.8所有要求的符合性。\n#### 5.9 相关方\n##### 5.9.1 承包商\n5.9.1.1 企业应建立承包商准入和退出标准，审查承包商的资质、安全业绩、人员资格与培训、安全管理机构设置等，选择合格的承包商，并建立合格承包商名录和档案。\n5.9.1.2 施工作业前，企业应审核承包商的施工方案，核实确认施工作业程序、安全风险评估结果、安全措施和应急预案。\n**5.9.1.3** 企业应对承包商作业人员进行安全交底，告知作业周边潜在的火灾、爆炸及有毒物质泄漏等安全风险、应急响应措施和要求，并对承包商作业过程实施监督检查。\n5.9.1.4 企业应定期对承包商进行业绩评价，根据评价结果及时调整或更换承包商。\n##### 5.9.2 供应商\n**5.9.2.1** 企业应对供应商选择、准入、续用等过程进行规范管理，明确质量技术要求、质量控制措施、验收检验标准和违约责任，定期识别与采购有关的安全风险。\n5.9.2.2 企业应按照物资入库验收标准，对采购物资实施质量检验或验收。\n5.9.2.3 企业应定期对供应商进行业绩评价，根据评价结果及时调整或更换供应商。\n##### 5.9.3 证实方法\n通过查验的方式，确定相关方安全管理制度和档案、安全管理协议、施工作业资料及现场、业绩评价等与5.9所有要求的符合性。\n#### 5.10 化学品安全和危险化学品重大危险源\n##### 5.10.1 鉴定分类和登记\n5.10.1.1 企业应建立化学品普查表，对产品、原辅料和中间产品进行普查，建立危险化学品管理档案。\n5.10.1.2 企业应按规定对危险化学品进行登记。\n5.10.1.3 企业应对生产的化学品进行物理危险性辨识，对危险性尚未确定的化学品应向鉴定机构申请鉴定。\n**5.10.1.4** 企业应根据化学品鉴定报告及其他物理危险性数据资料，编制化学品物理危险性分类报告，对分类属于危险化学品的进行登记。\n5.10.1.5 生产企业应设立或委托设立专用的24小时应急咨询服务电话，为危险化学品事故应急救援提供技术指导和必要的协助。",
#              "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"},
#            {"score": "0.51335686",
#             "content": "##### 5.6.7 安全设施\n5.6.7.1 企业应根据生产、储存环节涉及危险化学品的种类和危险特性设置安全设施。\n5.6.7.2 企业应根据危险化学品危险特性、工艺过程的复杂性以及安全风险评估结果，确定安全仪表配置范围和要求，选择符合安全性能要求的仪表设备，正确安装、调试、检测，达到投用条件，并对安全仪表系统进行安全完整性等级评估，满足预定的安全功能。\n5.6.7.3 企业应定期对安全设施进行维护、保养，对于老化、损坏无法修复或不符合标准要求的安全设施，及时报废和更换。\n##### 5.6.8 特种设备\n企业应对使用的特种设备办理使用登记，定期检验和维护保养，建立特种设备安全技术档案，配备符合资格要求的特种设备安全管理人员和作业人员。\n##### 5.6.9 设备完整性数据库\n5.6.9.1 企业应建立设备完整性数据库，将设备的基础数据、运行参数、检验检测数据、维修维护数据、缺陷数据等纳入数据库统一管理。\n5.6.9.2 企业应定期分析研究数据库中的各项数据，利用信息化手段和专业技术工具，做好设备分级管理、缺陷管理、检验检测、预防性维修等完整性工作。\n##### 5.6.10 证实方法\n通过查验的方式，确定设备检验检测计划和结果、检维修方案和情况、设备设施档案台账、缺陷台账、安全仪表完整性等级评估、安全仪表系统技术规格书等与5.6所有要求的符合性。\n#### 5.7 操作安全\n##### 5.7.1 操作规程\n5.7.1.1 企业应结合生产工艺、技术、设备设施特点和原材料、辅助材料、产品、中间产品的危险性编制操作规程。\n5.7.1.2 在新工艺、新技术、新装置、新产品投产或投用前，企业应组织编制操作规程。\n5.7.1.3 企业应至少每年评审操作规程的适应性和有效性，至少每三年修订操作规程，保证版本最新有效。\n##### 5.7.2 正常操作\n5.7.2.1 企业应根据操作规程中确定的重要控制指标编制工艺卡片，准确反映实际操作要求，操作人员应严格按照操作规程和工艺卡片执行。\n5.7.2.2 企业应明确岗位操作人员、专业技术人员巡回检查的管理要求，对生产装置、设备进行定时巡检。\n5.7.2.3 企业应对操作班组交接班进行规范管理，将异常工况、现场作业、存在的问题和隐患、需接续的工作等事项交接到位。\n##### 5.7.3 开停车管理\n5.7.3.1 企业在生产装置开停车前，应以危险、有害因素辨识和安全风险评估为基础编制开停车方\n案，经审批后实施。\n5.7.3.2 企业应根据开停车方案，组织编制相应的安全条件确认表，经专业技术人员逐项确认，有效落实各项安全措施。\n5.7.3.3 企业应建立停工交付检修、检修结束交付生产的交接管理机制。\n5.7.3.4 企业应严格控制生产装置开停车过程中现场人员数量。\n##### 5.7.4 报警管理\n5.7.4.1 企业应根据安全风险分析结果及工艺、设备的安全设计保护要求，确定需要设置的报警联锁及参数。\n5.7.4.2 企业应明确报警管理部门，根据报警后果严重性以及允许的响应时间确定报警优先级，明确关键报警，制定并实施报警分级管理制度。\n5.7.4.3 在发生报警时，企业应立即确认和响应，记录关键报警处置过程，分析报警原因。\n5.7.4.4 企业应定期对报警活动数据开展统计分析，评估并制定措施，减少不合理报警。\n##### 5.7.5 异常工况处置\n5.7.5.1 企业应辨识各类异常工况情形和处置过程中的安全风险，规范明晰异常工况处置程序，确保安全稳妥处置异常工况。\n5.7.5.2 企业应对异常工况下的应急处置进行授权，接到异常信息的人员及时有序处置。\n5.7.5.3 在异常工况处置过程中，企业应采用视频监控、电子围栏、基于人员定位系统的人员聚集风险监测预警等信息化智能化技术，严格管控现场人员，防止与处置无关的人员进入作业区域。\n5.7.5.4 在异常工况处置结束后，企业应进行溯源分析，完善工艺指标、操作规程、应急预案等相关内容。",
#             "file_id": "e5db4c98-7335-11f0-9f08-00163e0c52c3"}]
#     response = """危险化学品管理在港口行业中需遵循严格的安全规范，具体要求如下：
#
# 1. **化学品安全技术说明书与标签**
#    - 企业应为其生产、经营的化学品提供《化学品安全技术说明书》，并在包装上粘贴或拴挂符合GB15258要求的《安全标签》。
#    - 采购化学品时，需索取对应的《化学品安全技术说明书》和《安全标签》。
#
# 2. **储存安全管理**
#    - 危险化学品应按规定的储存方式和数量储存，并对出入库进行核查登记。
#    - 剧毒化学品和第一类非药品类易制毒化学品需实行**双人收发、双人保管**制度。
#    - 储存剧毒化学品的数量、地点及管理人员情况需按规定备案。
#
# 3. **重大危险源管理**
#    - 企业需按GB 18218辨识危险化学品重大危险源，建立档案并定期检测、评估、监控，制定应急预案。
#    - 涉及重大危险源的生产、储存装置需开展HAZOP分析，并设置安全仪表系统（SIS）等安全监控系统。
#    - 设备设施需定期检测、检验、维护，确保有效运行。
#
# 4. **作业安全管理**
#    - 特殊作业需实施作业许可管理，开展作业安全风险分析，并制定控制措施。
#    - 作业现场需设置监护人，确保应急通道畅通，并在作业完毕后及时清理现场。
#
# 5. **变更管理**
#    - 变更需分类分级管理，实施前需评估安全风险并落实控制措施，审批通过后方可执行。
#
# 6. **设备与设施管理**
#    - 安全仪表系统、气体检测报警系统、消防设施等需保持正常运行，定期检查维护。
#    - 特种设备需办理使用登记，定期检验并建立安全技术档案。
#
# 以上内容均需通过查验《化学品安全技术说明书》《危险化学品重大危险源档案》《作业许可记录》等资料及现场核实，确保符合相关规范。
#
# 若需进一步了解具体条款或操作细节，可结合实际场景提问，以便精准提供支持。"""
#     r = response_source_text(response, ref)
#     print(r)
